package com.lhkj.ct.meta.modules.psychology.model.vo;

import com.lhkj.ct.base.utils.MathUtil;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblScale;
import com.lhkj.ct.meta.modules.psychology.model.bo.DimensionBo;
import com.lhkj.ct.meta.modules.psychology.model.bo.DimensionGroupBo;
import com.lhkj.ct.meta.modules.psychology.model.dto.SampleAnalysisDto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.math3.distribution.FDistribution;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * <p>
 *     样本分析结果
 * </p>
 */
@Data
public class AnalysisResult implements Serializable {

    @ApiModelProperty("量表名称")
    private String scaleName;

    @ApiModelProperty("生成时间")
    private Date generationDate = new Date();

    @ApiModelProperty("描述统计")
    private List<Group> groupList = new ArrayList<>();

    @ApiModelProperty("差异分析")
    private List<Diversity> diversityList;

    @ApiModelProperty("结论")
    private String conclusion;

    @Data
    public static class Group{

        @ApiModelProperty("团体名称")
        private String groupName;

        @ApiModelProperty("维度统计")
        private List<DimenStatistics> dimenStatisticsList;

    }

    @Data
    public static class Diversity{

        @ApiModelProperty("维度名称")
        private String dimenName;

        @ApiModelProperty("区间")
        private List<Section> sectionList;

        @ApiModelProperty("F值")
        private Double F;

        @ApiModelProperty("P值")
        private String P;

        @ApiModelProperty("pValue值")
        private Double pValue;

        @Data
        @Accessors(chain = true)
        public static class Section{

            @ApiModelProperty("区间名称")
            private String sectionName;

            @ApiModelProperty("平方和")
            private Double SS;

            @ApiModelProperty("自由度")
            private Integer DF;

            @ApiModelProperty("均方")
            private Double MS;

        }
    }

    public void generateStatisticalDescription(List<SampleAnalysisDto.Combine> groups,
                                               List<DimensionBo> dimens,
                                               AnalysisResult vo, TblScale gaugeInfo,
                                               BiFunction<String, String, Map<String, Object>> biFunction){
        // 初始化所有样本数据
        List<DimensionBo> allDimenReport = new ArrayList<>();
        for (int i = 0; i < groups.size(); i++) {
            SampleAnalysisDto.Combine groupDto = groups.get(i);
            AnalysisResult.Group groupVo = new AnalysisResult.Group(); // 初始化团体分组
            groupVo.setGroupName(groupDto.getCombineName()); // 团体名称
            List<DimensionBo> dimenReports = new ArrayList<>(); // 初始化组内维度报告列表
            // step 3.2 遍历团体包含的报告
            for (String reportNum : groupDto.getReportIds()) {
                // 收集维度得分，用于总评
//                Map<String, DimenBo> params = new HashMap<>();
                // step 3.3 计算报告在各个维度上的分数
                Map<String, Object> formData = biFunction.apply(reportNum, gaugeInfo.getId());
                for (DimensionBo dimen : dimens) {
                    DimensionBo dimenBo = null;
                    if (dimen.getDimType() == 1){
                        dimen.computeDimScore(formData).setGroupId("GROUP_" + i);
                        dimenBo = dimen.clone();
                        formData.put(dimen.getDimName() + "原始分", dimenBo.getOriginalScore() == null ? BigDecimal.ZERO : dimenBo.getOriginalScore());
                        formData.put(dimen.getDimName(), dimenBo.getRealScore() == null ? BigDecimal.ZERO : dimenBo.getRealScore());
                    } else if (dimen.getDimType() == 2){
                        dimen.computeDimScore(formData).setGroupId("GROUP_" + i);
                        dimenBo = dimen.clone();
                    }
                    dimenReports.add(dimenBo);
                }
            }
            allDimenReport.addAll(dimenReports);
            groupVo.setDimenStatisticsList(gather(dimenReports));
            vo.getGroupList().add(groupVo);
        }
        vo.setDiversityList(vo.generateDiversity(allDimenReport));
    }

    /**
     *  聚合，计算团体在各个维度之间分数的平均分、标准差、中位数、最大值、最小值
     */
    private List<DimenStatistics> gather(List<DimensionBo> groupDimens) {
        // 根据维度名称分组
        return groupDimens.stream().collect(Collectors.groupingBy(DimensionBo::getDimName)).entrySet().stream().map(m -> {
            DimenStatistics dimenStatistics = new DimenStatistics();
            double doubleStream = m.getValue().stream().mapToDouble(p -> p.getRealScore().doubleValue()).average().getAsDouble();
            List<Double> collect = m.getValue().stream().mapToDouble(p -> p.getRealScore().doubleValue()).boxed().collect(Collectors.toList());
            dimenStatistics.setDimName(m.getKey())
                    .setAvg(MathUtil.getFloorNum(doubleStream, 2))
                    .setStd(MathUtil.getStandardDeviation( collect))
                    .setMedian(MathUtil.getMedian( collect))
                    .setMax( collect.stream().max(Comparator.comparing(x -> x)).orElse(0D))
                    .setMin(collect.stream().min(Comparator.comparing(x -> x)).orElse(0D));
            return dimenStatistics;
        }).collect(Collectors.toList());
    }

    /**
     * @Description 根据所有维度报告生成差异分析
     * @param dimens
     * @return
     */
    public List<AnalysisResult.Diversity> generateDiversity(List<DimensionBo> dimens) {
        // 先按照维度编号分组，在按照团体编号分组
        Map<String, Map<String, List<DimensionBo>>> collect = dimens
                .stream()
                .collect(Collectors.groupingBy(DimensionBo::getDimName, Collectors.groupingBy(DimensionBo::getGroupId)));
        // 处理每个维度的差异分析
        return collect.entrySet().stream().map(p -> {
            AnalysisResult.Diversity diversity = new AnalysisResult.Diversity();
            diversity.setDimenName(p.getKey());
            // 维度总平均分
            Double totalAvg = p.getValue().entrySet().stream().flatMap(e -> e.getValue().stream()).mapToDouble(p1 -> p1.getRealScore().doubleValue()).average().orElse(0);
            // 初始化该维度内样本总数 tips 用于计算总变异自由度
            AtomicInteger DFT = new AtomicInteger();
            // 各组的平均分、样本数、平方和
            List<DimensionGroupBo> groupsAvg = p.getValue().entrySet().stream().map(listEntry -> {
                DimensionGroupBo dimenGroupBo = new DimensionGroupBo();
                dimenGroupBo.setGroupId(listEntry.getKey());
                // 平均值
                double avg = listEntry.getValue().stream().mapToDouble(p1 -> p1.getRealScore().doubleValue()).average().orElse(0);
                dimenGroupBo.setAvg(avg);
                // 维度内某个团体的样本数
                dimenGroupBo.setSize(listEntry.getValue().size());
                // 平方和
                double ss = listEntry.getValue().stream().mapToDouble(d -> Math.pow(d.getRealScore().doubleValue() - avg, 2)).reduce(Double::sum).orElse(0);
                dimenGroupBo.setSs(ss);
                // 维度内样式总数
                DFT.addAndGet(listEntry.getValue().size());
                return dimenGroupBo;
            }).collect(Collectors.toList());
            // 组间变异平方和
            double SSA = 0;
            // 组内变异平方和
            double SSE = 0;
            for (DimensionGroupBo map : groupsAvg) {
                double avg = Math.pow(map.getAvg() - totalAvg, 2);
                SSA += avg * map.getSize();
                SSE += map.getSs();
            }
            // 组间变异自由度
            int DFA = p.getValue().size() - 1;
            int DFE = DFT.get() - 1 - DFA;
            // 组间变异均方
            Double MSA = MathUtil.divide(SSA,DFA,2).doubleValue();
            // 组内变异均方
            Double MSE = MathUtil.divide(SSE,DFT.get(),2).doubleValue();
            List<AnalysisResult.Diversity.Section> sectionList = new ArrayList<>();
            sectionList.add(new AnalysisResult.Diversity.Section().setSectionName("组间变异").setSS(SSA).setDF(DFA).setMS(MSA));
            sectionList.add(new AnalysisResult.Diversity.Section().setSectionName("组内变异").setSS(SSE).setDF(DFE).setMS(MSE));
            sectionList.add(new AnalysisResult.Diversity.Section().setSectionName("总变异").setSS(SSA + SSE).setDF(DFT.get() - 1));
            diversity.setSectionList(sectionList);
            // F值
            double F = MathUtil.divide(MSA, MSE, 2).doubleValue();
            diversity.setF(F);
            // P值
            FDistribution fd = new FDistribution(DFA, DFE);
            double P = 1.0 - fd.cumulativeProbability(F);
            if (P < 0.05) {
                diversity.setP("P < 0.05");
            }else if (P == 0.05){
                diversity.setP("P = 0.05");
            }else {
                diversity.setP("P > 0.05");
            }
            diversity.setPValue(P);
            return diversity;
        }).collect(Collectors.toList());
    }

}
